﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Shapes;
using System.Windows.Media;

namespace Tetris.Model
{
    public class TetrisModel : ITetrisModel
    {

        public TetrisModel(int blockWidth, int blockHeight)
        {
            this.AllBlocksCollection = new BlockCollectionModel();

            this.CurrentElement = new ElementModel(3, blockWidth, blockHeight);
            this.NextElement = new ElementModel(0, blockWidth, blockHeight);
            this.Score = 0;
            this.EndOfGame = false;

            this.BlockHeight = blockHeight;
            this.BlockWidth = blockWidth;
            this.CanvasHeight = 15*blockHeight;
            this.CanvasWidth = 10*blockWidth;
        }

        #region private fields

        //TODO kolekcja ma byc obserwowalna
        //to powinno byc publiczne view powinien obserwowac ta kolekcje
        public BlockCollectionModel AllBlocksCollection
        { 
            get; set;
        }   

        #endregion

        #region public fields

        #endregion

        #region private methods

        //chyba nie obejdzie bez kolekcji klockow jaka ma plansza, przed ruszeniem klock trzeba sprawdzic, czy jakis inny klocek nie jest
        //juz na danej pozycji
        
        private bool CanBlockMoveDown(IBlockModel blockModel)
        {
            var result = true;

            if( blockModel.Top + blockModel.Height >= this.CanvasHeight )
            {
                result = false;
            }
            else if( this.AllBlocksCollection.Contains( b => b.Top == blockModel.Top + blockModel.Height && b.Left == blockModel.Left ) )
            {
                result = false;
            }

            return result;
        }

        private bool CanBlockMoveLeft(IBlockModel blockModel)
        {
            var result = true;

            if (blockModel.Left - blockModel.Width < 0)
            {
                result = false;
            }
            else if (this.AllBlocksCollection.Contains(b => b.Left == blockModel.Left - blockModel.Width && b.Top == blockModel.Top) )
            {
                result = false;
            }

            return result;
        }

        private bool CanBlockMoveRight(IBlockModel blockModel)
        {
            var result = true;

            if(blockModel.Left + blockModel.Width >= this.CanvasWidth)
            {
                result = false;
            }
            else if (this.AllBlocksCollection.Contains(b => b.Left == blockModel.Left + blockModel.Width && b.Top == blockModel.Top) )
            {
                result = false;
            }

            return result;
        }

        private static bool CanElementMoveBase(Func<IElementModel, bool> action, IElementModel elementModel)
        {
            var result = true;

            if (action != null)
            {
                result = action.Invoke(elementModel);
            }

            return result;
        }

        private bool ExistFullLine(IBlockModel blockModel)
        {
            var result = false;
            var lineLength = this.CanvasWidth / this.BlockWidth;

            if (this.AllBlocksCollection.Count(x => x.Top == blockModel.Top) == lineLength)
            {
                result = true;
            }

            return result;
        }

        private void MoveDownBlocksAboveLine(int top)
        {
            var blocksAbove = (from b in this.AllBlocksCollection where b.Top < top select b).ToList();
            
            foreach (var block in blocksAbove)
            {
                MoveDownBlock(block);
            }
        }

        private static void MoveDownBlock(IBlockModel blockModel)
        {
            blockModel.Top += blockModel.Height;
        }

        private bool CanBlockRotate(IBlockModel blockModel)
        {
            return this.CanBlockMoveDown(blockModel) &&
                   this.CanBlockMoveLeft(blockModel) &&
                   this.CanBlockMoveRight(blockModel);
        }

        private void NotifyPropertyChanged(string property)
        {
            if(this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }

        #endregion

        #region ITetrisModel implementation

        public event PropertyChangedEventHandler PropertyChanged;

        private int _score;
        public int Score
        {
            get
            {
                return this._score;
            } 
            set
            {
                if(this._score != value)
                {
                    this._score = value;
                    this.NotifyPropertyChanged("Score");
                }
            }
        }

        public bool EndOfGame
        {
            get; private set;
        }

        public bool CanElementMoveDown(IElementModel elementModel)
        {
            return CanElementMoveBase(e => e.BlocksCollection.All(CanBlockMoveDown), elementModel);
        }

        public bool CanElementMoveLeft(IElementModel elementModel)
        {
            return CanElementMoveBase(e => e.BlocksCollection.All(CanBlockMoveLeft), elementModel);
        }

        public bool CanElementMoveRight(IElementModel elementModel)
        {
            return CanElementMoveBase(e => e.BlocksCollection.All(CanBlockMoveRight), elementModel);
        }

        public bool CanElementRotate(IElementModel elementModel)
        {
            var result = true;

            var element = new ElementModel();
            element.CopyFrom(this.CurrentElement);


            var x = element.BlocksCollection[0].Left;
            var y = element.BlocksCollection[0].Top;

            foreach (var blockModel in element.BlocksCollection)
            {
                blockModel.Left -= x;
                blockModel.Top -= y;
            }

            for (int i = 1; i < element.BlocksCollection.Count(); i++)
            {
                var tmp = element.BlocksCollection[i].Left;
                element.BlocksCollection[i].Left = -1 * element.BlocksCollection[i].Top;
                element.BlocksCollection[i].Top = tmp;
            }

            foreach (var blockModel in element.BlocksCollection)
            {
                blockModel.Left += x;
                blockModel.Top += y;

                if(this.AllBlocksCollection.Contains(b => b.Left == blockModel.Left && b.Top == blockModel.Top) )
                {
                    result = false;
                    break;
                }
                if(blockModel.Left < 0 || blockModel.Left >= this.CanvasWidth)
                {
                    result = false;
                    break;
                }
                if(blockModel.Top >= this.CanvasHeight)
                {
                    result = false;
                    break;
                }
            }

            return result;
        }

        public int CanvasHeight
        {
            get;
            private set;
        }

        public int CanvasWidth
        {
            get;
            private set;
        }

        private SolidColorBrush _canvasColor;
        public SolidColorBrush CanvasColor
        {
            get
            {
                if( this._canvasColor == null )
                {
                    this._canvasColor = new SolidColorBrush(Colors.LightGray);
                }
                return this._canvasColor;
            }
        }

        public int BlockHeight
        {
            get;
            private set;
        }

        public int BlockWidth
        {
            get;
            private set;
        }

        public IElementModel NextElement
        { 
            get;
            private set;
        }

        public IElementModel CurrentElement
        { 
            get;private set;
        }

        public IList<IList<int>> Lines
        {
            get { throw new NotImplementedException(); }
        }

        public void MoveLeft()
        {
            foreach (var block in CurrentElement.BlocksCollection)
            {
                //tutaj trzeba by sprawdzic czy jak sie idzie w lewo to czy klocek sie zmiesci
                block.Left -= block.Width;
            }
        }
        public void MoveRight()
        {
            foreach (var block in CurrentElement.BlocksCollection)
            {
                //tutaj trzeba by sprawdzic czy jak sie idzie w prawo to czy klocek sie zmiesci
                block.Left += block.Width;
            }
        }
        public void MoveDown()
        {
            foreach (var block in this.CurrentElement.BlocksCollection)
            {
                block.Top += block.Height;
            }
        }
        public void Rotate()
        {
            this.CurrentElement.Rotate();
        }       

        public void GetNextElement()
        {
            this.CurrentElement =  new ElementModel();
            this.CurrentElement.CopyFrom(this.NextElement);

            var rand = new Random();
            var type = rand.Next(4);

            this.NextElement = new ElementModel(type, this.BlockWidth, this.BlockHeight);
        }

        public void AddLastBlockToCollection() //poloz na planszy ostatni klocek 
        {
            foreach (var block in this.CurrentElement.BlocksCollection)
            {
                if (block.Top > 0)
                {
                    this.AllBlocksCollection.Add(block);
                }
                else
                {
                    this.EndOfGame = true;
                }
            }    
        }

        public bool DeleteFullLines(IElementModel elementModel)
        {
            var result = false;

            foreach (var block in elementModel.BlocksCollection)
            {
                if( this.ExistFullLine(block) )
                {
                    this.Score++;
                    this.AllBlocksCollection.DeleteLine(block.Top);
                    //teraz trzeba przesunac w dol wszystkie klocki powyzej linii
                    this.MoveDownBlocksAboveLine(block.Top);

                    result = true;
                }
            }

            return result;
        }

        #endregion
    }
}
